import { render, screen } from '@testing-library/react';
import { http, HttpResponse } from 'msw';

import { server } from '@/setup-tests/server';
import { withTestQueryProvider } from '@/react/test-utils/withTestQuery';
import { withUserProvider } from '@/react/test-utils/withUserProvider';
import { withTestRouter } from '@/react/test-utils/withRouter';
import { Stack, StackStatus, StackType } from '@/react/common/stacks/types';

import { StackInfoTab } from './StackInfoTab';

vi.mock('@/react/hooks/useEnvironmentId', () => ({
  useEnvironmentId: vi.fn(() => 1),
}));

// Mock the child components to isolate testing
vi.mock('./AssociateStackForm', () => ({
  AssociateStackForm: vi.fn(() => (
    <div data-cy="associate-stack-form">AssociateStackForm</div>
  )),
}));

vi.mock('./StackActions', () => ({
  StackActions: vi.fn(() => <div data-cy="stack-actions">StackActions</div>),
}));

vi.mock(
  '@/react/common/stacks/ItemView/StackDuplicationForm/StackDuplicationForm',
  () => ({
    StackDuplicationForm: vi.fn(() => (
      <div data-cy="stack-duplication-form">StackDuplicationForm</div>
    )),
  })
);

vi.mock('./StackRedeployGitForm/StackRedeployGitForm', () => ({
  StackRedeployGitForm: vi.fn(() => (
    <div data-cy="stack-redeploy-git-form">StackRedeployGitForm</div>
  )),
}));

describe('StackInfoTab', () => {
  describe('initial rendering', () => {
    it('should render stack name', () => {
      renderComponent({ stackName: 'my-test-stack' });

      expect(screen.getByText('my-test-stack')).toBeVisible();
    });

    it('should render StackActions when stack exists', () => {
      const mockStack = createMockStack();
      renderComponent({ stack: mockStack });

      expect(screen.getByTestId('stack-actions')).toBeVisible();
    });

    it('should not render StackActions when stack is undefined', () => {
      renderComponent({ stack: undefined });

      expect(screen.queryByTestId('stack-actions')).not.toBeInTheDocument();
    });

    it('should render stack details section', () => {
      renderComponent();

      expect(screen.getByText('Stack details')).toBeVisible();
    });
  });

  describe('external and orphaned warnings', () => {
    it('should show external stack warning when isExternal is true', () => {
      renderComponent({ isExternal: true });

      expect(
        screen.getByText(/This stack was created outside of Portainer/i)
      ).toBeVisible();
      expect(screen.getByText('Information')).toBeVisible();
    });

    it('should show orphaned stack warning when isOrphaned is true', () => {
      renderComponent({ isOrphaned: true });

      expect(screen.getByText(/This stack is orphaned/i)).toBeVisible();
      expect(screen.getByText(/Associate to this environment/i)).toBeVisible();
    });

    it('should show orphaned warning when isOrphanedRunning is true', () => {
      renderComponent({ isOrphanedRunning: true, isOrphaned: false });

      expect(screen.getByText(/This stack is orphaned/i)).toBeVisible();
    });

    it('should show orphaned warning when both isOrphaned and isOrphanedRunning are true', () => {
      renderComponent({ isOrphaned: true, isOrphanedRunning: true });

      expect(screen.getByText(/This stack is orphaned/i)).toBeVisible();
    });

    it('should show both warnings when isExternal and isOrphaned are true', () => {
      renderComponent({ isExternal: true, isOrphaned: true });

      expect(
        screen.getByText(/This stack was created outside of Portainer/i)
      ).toBeVisible();
      expect(screen.getByText(/This stack is orphaned/i)).toBeVisible();
    });

    it('should not show warnings when both isExternal and isOrphaned are false', () => {
      renderComponent({ isExternal: false, isOrphaned: false });

      expect(screen.queryByText('Information')).not.toBeInTheDocument();
      expect(
        screen.queryByText(/This stack was created outside of Portainer/i)
      ).not.toBeInTheDocument();
      expect(
        screen.queryByText(/This stack is orphaned/i)
      ).not.toBeInTheDocument();
    });
  });

  describe('conditional form rendering', () => {
    it('should render AssociateStackForm when stack is orphaned', () => {
      const mockStack = createMockStack();
      renderComponent({
        stack: mockStack,
        isOrphaned: true,
      });

      expect(screen.getByTestId('associate-stack-form')).toBeVisible();
      expect(
        screen.queryByTestId('stack-duplication-form')
      ).not.toBeInTheDocument();
      expect(
        screen.queryByTestId('stack-redeploy-git-form')
      ).not.toBeInTheDocument();
    });

    it('should not render AssociateStackForm when only isOrphanedRunning is true', () => {
      const mockStack = createMockStack();
      renderComponent({
        stack: mockStack,
        isOrphanedRunning: true,
        isOrphaned: false,
      });

      // isOrphanedRunning alone doesn't trigger the form, only isOrphaned does
      expect(
        screen.queryByTestId('associate-stack-form')
      ).not.toBeInTheDocument();
      // Should show duplication form instead if regular
      expect(screen.getByTestId('stack-duplication-form')).toBeVisible();
    });

    it('should render StackRedeployGitForm when stack has GitConfig and is not from template', () => {
      const mockStack = createMockStack({
        GitConfig: {
          URL: 'https://github.com/test/repo',
          ReferenceName: 'main',
          ConfigFilePath: 'docker-compose.yml',
          ConfigHash: '',
          TLSSkipVerify: false,
        },
        FromAppTemplate: false,
      });
      renderComponent({
        stack: mockStack,
        isOrphaned: false,
        isRegular: true,
      });

      expect(screen.getByTestId('stack-redeploy-git-form')).toBeVisible();
    });

    it('should not render StackRedeployGitForm when stack is from app template', () => {
      const mockStack = createMockStack({
        GitConfig: {
          URL: 'https://github.com/test/repo',
          ReferenceName: 'main',
          ConfigFilePath: 'docker-compose.yml',
          ConfigHash: '',
          TLSSkipVerify: false,
        },
        FromAppTemplate: true,
      });
      renderComponent({
        stack: mockStack,
        isOrphaned: false,
        isRegular: true,
      });

      expect(
        screen.queryByTestId('stack-redeploy-git-form')
      ).not.toBeInTheDocument();
    });

    it('should not render StackRedeployGitForm when stack has no GitConfig', () => {
      const mockStack = createMockStack({
        GitConfig: undefined,
      });
      renderComponent({
        stack: mockStack,
        isOrphaned: false,
        isRegular: true,
      });

      expect(
        screen.queryByTestId('stack-redeploy-git-form')
      ).not.toBeInTheDocument();
    });

    it('should render StackDuplicationForm when stack is regular and not orphaned', () => {
      const mockStack = createMockStack();
      renderComponent({
        stack: mockStack,
        isRegular: true,
        isOrphaned: false,
      });

      expect(screen.getByTestId('stack-duplication-form')).toBeVisible();
    });

    it('should not render StackDuplicationForm when stack is not regular', () => {
      const mockStack = createMockStack();
      renderComponent({
        stack: mockStack,
        isRegular: false,
        isOrphaned: false,
      });

      expect(
        screen.queryByTestId('stack-duplication-form')
      ).not.toBeInTheDocument();
    });

    it('should not render any forms when stack is undefined', () => {
      renderComponent({ stack: undefined });

      expect(
        screen.queryByTestId('associate-stack-form')
      ).not.toBeInTheDocument();
      expect(
        screen.queryByTestId('stack-duplication-form')
      ).not.toBeInTheDocument();
      expect(
        screen.queryByTestId('stack-redeploy-git-form')
      ).not.toBeInTheDocument();
    });
  });

  describe('git and duplication form combination', () => {
    it('should render both StackRedeployGitForm and StackDuplicationForm when conditions met', () => {
      const mockStack = createMockStack({
        GitConfig: {
          URL: 'https://github.com/test/repo',
          ReferenceName: 'main',
          ConfigFilePath: 'docker-compose.yml',
          ConfigHash: '',
          TLSSkipVerify: false,
        },
        FromAppTemplate: false,
      });
      renderComponent({
        stack: mockStack,
        isRegular: true,
        isOrphaned: false,
      });

      expect(screen.getByTestId('stack-redeploy-git-form')).toBeVisible();
      expect(screen.getByTestId('stack-duplication-form')).toBeVisible();
    });
  });

  describe('stack file content and environment id passing', () => {
    it('should pass stackFileContent to child components', () => {
      const mockStack = createMockStack();
      const stackFileContent =
        'version: "3"\nservices:\n  web:\n    image: nginx';
      renderComponent({
        stack: mockStack,
        stackFileContent,
        isRegular: true,
      });

      expect(screen.getByTestId('stack-actions')).toBeVisible();
      expect(screen.getByTestId('stack-duplication-form')).toBeVisible();
    });

    it('should pass environmentId to child components', () => {
      const mockStack = createMockStack();
      renderComponent({
        stack: mockStack,
        environmentId: 42,
        isRegular: true,
      });

      expect(screen.getByTestId('stack-actions')).toBeVisible();
    });
  });
});

function renderComponent({
  stack,
  stackName = 'test-stack',
  stackFileContent,
  isRegular = true,
  isExternal = false,
  isOrphaned = false,
  isOrphanedRunning = false,
  environmentId = 1,
  yamlError,
}: Partial<React.ComponentProps<typeof StackInfoTab>> = {}) {
  // Mock the Docker API version endpoint
  server.use(
    http.get('/api/endpoints/:id/docker/version', () =>
      HttpResponse.json({ ApiVersion: '1.41' })
    )
  );

  const Wrapped = withTestQueryProvider(
    withTestRouter(withUserProvider(StackInfoTab))
  );

  return render(
    <Wrapped
      stack={stack}
      stackName={stackName}
      stackFileContent={stackFileContent}
      isRegular={isRegular}
      isExternal={isExternal}
      isOrphaned={isOrphaned}
      isOrphanedRunning={isOrphanedRunning}
      environmentId={environmentId}
      yamlError={yamlError}
    />
  );
}

function createMockStack(overrides?: Partial<Stack>): Stack {
  return {
    Id: 1,
    Name: 'test-stack',
    Type: StackType.DockerCompose,
    EndpointId: 1,
    SwarmId: '',
    EntryPoint: 'docker-compose.yml',
    Env: [],
    Status: StackStatus.Active,
    ProjectPath: '/data/compose/1',
    CreationDate: Date.now(),
    CreatedBy: 'admin',
    UpdateDate: Date.now(),
    UpdatedBy: 'admin',
    FromAppTemplate: false,
    IsComposeFormat: true,
    FilesystemPath: '/data/compose/1',
    StackFileVersion: '3',
    PreviousDeploymentInfo: null,
    SupportRelativePath: false,
    ...overrides,
  };
}
